from django.utils import timezone
from django.db import transaction
from analysis.models import KnowledgeMasteryProfile, KnowledgePracticeRecord
from score.models import ExamRecord, ScoreLossDetail, KnowledgePoint
import math


class ProfileService:
    """用户知识点画像服务 - 核心业务逻辑"""

    def update_mastery_from_exam(self, student, exam_record, loss_details):
        """
        根据考试记录更新知识点掌握度 - 主要入口方法
        """
        with transaction.atomic():
            # 1. 提取考试涉及的知识点
            knowledge_points = self._extract_knowledge_points_from_loss(loss_details)

            # 2. 为每个知识点创建练习记录
            practice_records = self._create_practice_records(
                student, exam_record, knowledge_points, loss_details
            )

            # 3. 更新掌握度画像
            updated_profiles = []
            for point in knowledge_points:
                profile = self._update_knowledge_profile(
                    student, point, practice_records, exam_record
                )
                updated_profiles.append(profile)

            return updated_profiles

    def _extract_knowledge_points_from_loss(self, loss_details):
        """从失分记录中提取知识点"""
        knowledge_points = set()

        for loss in loss_details:
            if loss.knowledge_point:
                knowledge_points.add(loss.knowledge_point)
            # 注意：自定义知识点已在ScoreLossDetail.save()中处理

        return list(knowledge_points)

    def _create_practice_records(self, student, exam_record, knowledge_points, loss_details):
        """创建知识点练习记录"""
        practice_records = []

        # 按知识点分组失分记录
        loss_by_knowledge = {}
        for loss in loss_details:
            point = loss.knowledge_point
            if point not in loss_by_knowledge:
                loss_by_knowledge[point] = []
            loss_by_knowledge[point].append(loss)

        # 为每个知识点创建练习记录
        for point in knowledge_points:
            point_losses = loss_by_knowledge.get(point, [])

            # 计算该知识点的练习结果
            result, score_impact, time_spent = self._calculate_practice_result(point_losses)

            practice_record = KnowledgePracticeRecord.objects.create(
                student=student,
                knowledge_point=point,
                exam_record=exam_record,
                result=result,
                score_impact=score_impact,
                time_spent=time_spent,
                question_difficulty=point.difficulty or 0.5,
                complexity_level=self._estimate_complexity(point_losses),
                practiced_at=exam_record.created_at or timezone.now()
            )
            practice_records.append(practice_record)

        return practice_records

    def _calculate_practice_result(self, loss_details):
        """根据失分记录计算练习结果"""
        if not loss_details:
            return 'correct', 0, None

        # 计算总失分
        total_loss = sum(loss.points for loss in loss_details)

        if total_loss == 0:
            return 'correct', 0, None
        elif all(loss.is_understand for loss in loss_details):
            return 'partial', -total_loss, None
        else:
            return 'incorrect', -total_loss, None

    def _estimate_complexity(self, loss_details):
        """估计题目复杂度"""
        if not loss_details:
            return 1

        # 基于失分类型判断复杂度
        complex_errors = ['concept_not_understood', 'method_error', 'process_error']
        error_types = [loss.loss_type for loss in loss_details]

        complex_count = sum(1 for et in error_types if et in complex_errors)

        if complex_count >= 2:
            return 3  # 高复杂度
        elif complex_count >= 1:
            return 2  # 中等复杂度
        else:
            return 1  # 基础复杂度

    def _update_knowledge_profile(self, student, knowledge_point, practice_records, exam_record):
        """更新单个知识点的掌握度画像"""
        # 获取该知识点的相关练习记录
        point_practices = [pr for pr in practice_records if pr.knowledge_point == knowledge_point]

        if not point_practices:
            return None

        # 获取或创建画像
        profile, created = KnowledgeMasteryProfile.objects.get_or_create(
            student=student,
            knowledge_point=knowledge_point,
            defaults={
                'first_encountered': exam_record.created_at or timezone.now(),
                'learning_status': 'learning'
            }
        )

        # 更新统计信息
        latest_practice = point_practices[0]  # 按时间排序的第一个是最新的

        profile.total_practices += len(point_practices)
        profile.correct_practices += sum(1 for p in point_practices if p.result == 'correct')
        profile.total_exposure += 1  # 每次考试算一次曝光

        # 计算新的掌握度 (简化版IRT)
        profile.mastery_level = self._calculate_mastery_level(profile, point_practices)

        # 更新其他字段
        profile.last_practiced = latest_practice.practiced_at
        profile.mastery_trend = self._calculate_mastery_trend(profile, point_practices)
        profile.actual_difficulty = self._calculate_actual_difficulty(profile)
        profile.learning_status = self._determine_learning_status(profile)
        profile.study_priority = self._calculate_study_priority(profile)
        profile.next_review_date = self._calculate_next_review_date(profile)

        profile.save()
        return profile

    def _calculate_mastery_level(self, profile, practice_records):
        """计算掌握度 - 简化IRT实现"""
        if profile.total_practices == 0:
            return 0.0

        # 基础正确率
        base_accuracy = profile.correct_practices / profile.total_practices

        # 考虑题目难度加权
        difficulty_weighted = 0.0
        total_weight = 0.0

        for practice in practice_records:
            weight = practice.question_difficulty  # 难题权重高
            score = 1.0 if practice.result == 'correct' else 0.0
            difficulty_weighted += score * weight
            total_weight += weight

        weighted_accuracy = difficulty_weighted / total_weight if total_weight > 0 else base_accuracy

        # 结合基础正确率和难度加权正确率
        final_mastery = (base_accuracy * 0.3 + weighted_accuracy * 0.7)

        # 应用置信度调整（数据量越多越可信）
        confidence = min(1.0, profile.total_practices / 10.0)  # 10次练习后达到最大置信度
        profile.confidence = confidence

        return final_mastery

    def _calculate_mastery_trend(self, profile, recent_practices):
        """计算掌握度趋势"""
        if len(recent_practices) < 2:
            return 'stable'

        # 分析最近几次练习的正确率趋势
        recent_correct = sum(1 for p in recent_practices[:3] if p.result == 'correct')
        recent_total = min(3, len(recent_practices))

        recent_accuracy = recent_correct / recent_total if recent_total > 0 else 0

        if recent_accuracy > profile.mastery_level + 0.1:
            return 'improving'
        elif recent_accuracy < profile.mastery_level - 0.1:
            return 'declining'
        else:
            return 'stable'

    def _calculate_actual_difficulty(self, profile):
        """计算实际难度"""
        if profile.total_practices == 0:
            return 0.5

        # 基于错误率计算难度
        error_rate = 1 - (profile.correct_practices / profile.total_practices)
        return error_rate

    def _determine_learning_status(self, profile):
        """确定学习状态"""
        if profile.total_practices == 0:
            return 'not_started'
        elif profile.mastery_level >= 0.8:
            return 'mastered'
        elif profile.mastery_level >= 0.6:
            return 'reviewing'
        elif profile.mastery_trend == 'declining':
            return 'struggling'
        else:
            return 'learning'

    def _calculate_study_priority(self, profile):
        """计算学习优先级"""
        if profile.mastery_level >= 0.8:
            return 1  # 低优先级
        elif profile.learning_status == 'struggling':
            return 5  # 高优先级
        elif profile.mastery_level <= 0.4:
            return 4  # 中高优先级
        else:
            return 3  # 中等优先级

    def _calculate_next_review_date(self, profile):
        """计算下次复习日期 - 基于记忆曲线"""
        if profile.mastery_level >= 0.8:
            # 掌握度高的知识点，复习间隔长
            interval_days = 14
        elif profile.mastery_level >= 0.6:
            interval_days = 7
        else:
            interval_days = 3

        return timezone.now() + timezone.timedelta(days=interval_days)

    def get_student_profile(self, student):
        """获取学生的完整知识点画像"""
        return KnowledgeMasteryProfile.objects.filter(
            student=student
        ).select_related('knowledge_point', 'knowledge_point__subject')

    def get_weak_points(self, student, threshold=0.6):
        """获取薄弱知识点"""
        return self.get_student_profile(student).filter(
            mastery_level__lt=threshold
        ).order_by('mastery_level')